package com.oromwin.repos.thread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

import com.oromwin.repos.common.Server.LockType;

public class ProcessManager implements Runnable {
    private static int fid = 0;
    private static final String fname = "/home/psyoblade/tmp";
    private static String _type = null;
    private static Map<Integer,LockObject> _lock = new HashMap<Integer, LockObject>();

    public ProcessManager(String type) {
        _type = type;
    }

    public static int getFid() {
        return fid++;
    }
    
    public static String getFname() {
        return fname;
    }
    
    public static String getDatetime() {
        SimpleDateFormat ISO8601UTC = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SSS'Z'");
        ISO8601UTC.setTimeZone(TimeZone.getTimeZone("UTC"));
        String now = ISO8601UTC.format(new Date());
        return now;
    }
    
    public enum Type { A, B, C, D, E, F }
    
    public class Request {
        private LockType _lockType = null;
        private Integer _fid = 0;
        private String _fname = null;
        public Request(LockType lockType, int fid, String fname) {
            _lockType = lockType;
            _fid = new Integer(fid);
            _fname = fname;
        }
        public LockType getlockType() {
            return _lockType;
        }
        public int getFid() {
            return _fid;
        }
        public String getFname() {
            return _fname;
        }
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("\ndatetime:" + getDatetime());
            sb.append(", lockType:" + _lockType.toString());
            sb.append(", fid:" + _fid.toString());
            sb.append(", fname:" + _fname);
            return sb.toString();
        }
    }
    
    protected class LockObject {
        private LockType _lockType = null;
        public LockObject(LockType lockType) {
            _lockType = lockType;
        }
        public LockType getLockType() {
            return _lockType;
        }
    }
    
    @Override
    public void run() {
        ProcessThread th = null;
        int sameFid = -1;
        Request[] requests = null;
        Request read = null;
        Request write = null; 
        Request read1 = null;
        Request read2 = null;
        Request read3 = null;
        Request write1 = null;
        Request write2 = null;
        Request write3 = null;
        
        switch (Type.valueOf(_type)) {
        
        case A: // write[fid#1] + read[fid#1] - contention occur
            System.out.println("processing A type");
            sameFid = getFid();
            write = new Request(LockType.WRITE, sameFid, getFname()); // current-write-object
            read = new Request(LockType.READ, sameFid, getFname()); // current-write-object
            requests = new Request[2];
            requests[0] = write;
            requests[1] = read;
            
            for (Request req : requests) {
                LockObject lockObject = null;
                synchronized (_lock) {
                    Integer fid = new Integer(req.getFid());
                    lockObject = _lock.get(fid);
                    if (lockObject == null) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else if (lockObject.getLockType() == LockType.READ && req.getlockType() == LockType.READ) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else {
                        System.out.println("same lock object returned. lock wating...");
                    }
                }
                th = new ProcessThread(req, _lock, lockObject); // need to pass client input/output socket stream also
                th.start();
            }
            break;
            
        case B: // read[fid#1] + read[fid#1] - no contention occur
            System.out.println("processing B type");
            sameFid = getFid();
            read1 = new Request(LockType.READ, sameFid, getFname());
            read2 = new Request(LockType.READ, sameFid, getFname());
            requests = new Request[2];
            requests[0] = read1;
            requests[1] = read2;
            
            for (Request req : requests) {
                LockObject lockObject = null;
                synchronized (_lock) {
                    Integer fid = new Integer(req.getFid());
                    lockObject = _lock.get(fid);
                    if (lockObject == null) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else if (lockObject.getLockType() == LockType.READ && req.getlockType() == LockType.READ) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else {
                        System.out.println("same lock object returned. lock wating...");
                    }
                }
                th = new ProcessThread(req, _lock, lockObject); // need to pass client input/output socket stream also
                th.start();
            }
            break;
        case C: // read[fid#1] + write[fid#1] - contention occur
            System.out.println("processing C type");
            sameFid = getFid();
            read = new Request(LockType.READ, sameFid, getFname());
            write = new Request(LockType.WRITE, sameFid, getFname());
            requests = new Request[2];
            requests[0] = read;
            requests[1] = write;
            
            for (Request req : requests) {
                LockObject lockObject = null;
                synchronized (_lock) {
                    Integer fid = new Integer(req.getFid());
                    lockObject = _lock.get(fid);
                    if (lockObject == null) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else if (lockObject.getLockType() == LockType.READ && req.getlockType() == LockType.READ) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else {
                        System.out.println("same lock object returned. lock wating...");
                    }
                }
                th = new ProcessThread(req, _lock, lockObject); // need to pass client input/output socket stream also
                th.start();
            }
            break;
        case D: // read[fid#1] + read[fid#1] + read[fid#1] ... - no contention
            System.out.println("processing B type");
            sameFid = getFid();
            read1 = new Request(LockType.READ, sameFid, getFname());
            read2 = new Request(LockType.READ, sameFid, getFname());
            read3 = new Request(LockType.READ, sameFid, getFname());
            requests = new Request[3];
            requests[0] = read1;
            requests[1] = read2;
            requests[2] = read3;
            
            for (Request req : requests) {
                LockObject lockObject = null;
                synchronized (_lock) {
                    Integer fid = new Integer(req.getFid());
                    lockObject = _lock.get(fid);
                    if (lockObject == null) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else if (lockObject.getLockType() == LockType.READ && req.getlockType() == LockType.READ) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else {
                        System.out.println("same lock object returned. lock wating...");
                    }
                }
                th = new ProcessThread(req, _lock, lockObject); // need to pass client input/output socket stream also
                th.start();
            }
            break;
        case E: // write[fid#1] + write[fid#2] - no contention
            System.out.println("processing E type");
            write1 = new Request(LockType.WRITE, getFid(), getFname());
            write2 = new Request(LockType.WRITE, getFid(), getFname());
            write3 = new Request(LockType.WRITE, getFid(), getFname());
            requests = new Request[3];
            requests[0] = write1;
            requests[1] = write2;
            requests[2] = write3;
            
            for (Request req : requests) {
                LockObject lockObject = null;
                synchronized (_lock) {
                    Integer fid = new Integer(req.getFid());
                    lockObject = _lock.get(fid);
                    if (lockObject == null) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else if (lockObject.getLockType() == LockType.READ && req.getlockType() == LockType.READ) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else {
                        System.out.println("same lock object returned. lock wating...");
                    }
                }
                th = new ProcessThread(req, _lock, lockObject); // need to pass client input/output socket stream also
                th.start();
            }
            break;
        case F: // write[fid#1] + read[fid#2] + write[fid#3] + read[fid#4] - no contention
            System.out.println("processing E type");
            write1 = new Request(LockType.WRITE, getFid(), getFname());
            read1 = new Request(LockType.READ, getFid(), getFname());
            write2 = new Request(LockType.WRITE, getFid(), getFname());
            read2 = new Request(LockType.READ, getFid(), getFname());
            requests = new Request[4];
            requests[0] = write1;
            requests[1] = read1;
            requests[2] = write2;
            requests[3] = read2;
            
            for (Request req : requests) {
                LockObject lockObject = null;
                synchronized (_lock) {
                    Integer fid = new Integer(req.getFid());
                    lockObject = _lock.get(fid);
                    if (lockObject == null) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else if (lockObject.getLockType() == LockType.READ && req.getlockType() == LockType.READ) {
                        lockObject = new LockObject(req.getlockType());
                        _lock.put(fid, lockObject);
                        
                    } else {
                        System.out.println("same lock object returned. lock wating...");
                    }
                }
                th = new ProcessThread(req, _lock, lockObject); // need to pass client input/output socket stream also
                th.start();
            }
            break;
        }
    }

    public static void main(String[] argv) {
        if (argv.length == 1) {
            String type = argv[0];
            ProcessManager pm = new ProcessManager(type);
            pm.run();
        } else {
            System.out.println("Illegal arguement");
        }
        return;
    }

}

